package org.activequant.math.algorithms;


/**
 * Accumulates data and computes mean and variance (incrementally) using
 * exponentially-weighted moving average (EMA). The mean and variance 
 * are unbiased if Input data is not correlated. Practically, make sure that
 * averaging period is much greater than auto-correlation lag (then the approximation 
 * of "uncorrelated" random data holds).<br/>
 * A subclass of the EMAAccumulator. Holds the following associated variable:
 * <ul>
 * <li>variance(EMAAccumulator)</li>
 * </ul>
 * Holds the following inherited variables:
 * <ul>
 * <li>period(int)</li>
 * <li>value(double)</li>
 * <li>numSamples(int)</li>
 * <li>lambda(double)</li>
 * <li>correction(double)</li>
 * </ul>
 * <p>
 * <b>History:</b><br>
 *  - [01.02.2008] Created (Mike Kroutikov)<br>
 *
 *	@see EMAAccumulator
 *  @author Mike Kroutikov
 */
public class EMAVarAccumulator extends EMAAccumulator {
	/**
	 * private final EMAAccumulator variance   = new EMAAccumulator();
	 */
	private final EMAAccumulator variance   = new EMAAccumulator();
	/**
	 * <strong>1.</strong> sets the associated/inherited period(int) and lambda(double) with the given averagingPeriod(int) and 1-(1/averagingPeriod)<br/>
	 * <strong>2.</strong> sets the period(int) and lambda(double) of the associated variance(EMAAccumulator) with the given averagingPeriod(int) and 1-(1/averagingPeriod)
	 */
	@Override
	public void setPeriod(int averagingPeriod) {
		if(averagingPeriod <= 1) {
			throw new IllegalArgumentException("period for variance computation can not be less than 2. :" + averagingPeriod);
		}
		super.setPeriod(averagingPeriod);
		variance.setPeriod(averagingPeriod);
	}

	/**
	 * Variance: Sqrt(<<(x-mu)*(x-mu)>>), where mu is the mean value, x - measurements.
	 * 
	 * @return variance.
	 */
	public double getVariance() {
		return Math.sqrt(getVariance2());
	}
	
	/**
	 * Squared variance: <<(x-mu)*(x-mu)>>, where mu is the mean value, x - measurements.
	 * 
	 * @return squared variance.
	 */
	public double getVariance2() {
		double lambda = 1. - 1. / getPeriod();
		return variance.getMeanValue() * (1. + lambda) / lambda / lambda / 2.0;
	}
	/**
	 * accumulates the given value(double) both in this EMAAccumulator and then accumulates the delta into the associated variance(EMAAccumulator)
	 */
	@Override
	public void accumulate(double value) {
		double delta = value - getMeanValue();
		
		super.accumulate(value);
		
		delta *= value - getMeanValue();
		
		variance.accumulate(delta);
	}
}
